package pl.pw.gis.gc_sa.tests;

import java.util.Random;
import java.util.Vector;

import org.junit.BeforeClass;
import org.junit.Test;

import pl.pw.gis.gc_sa.algo.Cost;
import pl.pw.gis.gc_sa.algo.CostFromArticle;
import pl.pw.gis.gc_sa.data.Graph;
import pl.pw.gis.gc_sa.data.VertexChange;
import pl.pw.gis.gc_sa.utils.Utils;

/**
 * Klasa zawierajaca testy wykonywane przy pomocy biblioteki jUnit.
 * 
 * W tej klasie testowane jest szybkosc dzialania dwoch sposobow obliczania
 * kosztu kolorowania grafu - przy pomocy obiektu CostFromArticle oraz metody
 * naiveArticleCostFunction
 * 
 * @author Lukasz
 * 
 */
public class ArticleCostFunctionsSpeedComparison {
    static Random random;
    static Vector<Graph> graphs;

    int noIterationsPerGraph = 1000;

    @BeforeClass
    public static void initialize() throws Exception {
        random = new Random();
        graphs = new Vector<Graph>();

        graphs.add(Graph.createVPGraph(50, 1));
        graphs.add(Graph.createVPGraph(100, 1));
        graphs.add(Graph.createVPGraph(500, 1));
        graphs.add(Graph.createVPGraph(50, 0.5f));
        graphs.add(Graph.createVPGraph(100, 0.5f));
        graphs.add(Graph.createVPGraph(500, 0.5f));
        graphs.add(Graph.createVPGraph(1000, 0.5f));
    }

    @Test
    public void bigTest() throws Exception {
        long cost2TotalTime = 0;
        long articleFuncTotalTime = 0;
        long cost2CurrentTime = 0;
        long articleFuncCurrentTime = 0;
        long startTime;
        int[] cost2Results;
        int[] articleFuncResults;

        for (Graph g : graphs) {
            Utils.log("Testing " + g.getGraphInfo());
            int[] solution = initLegal(g.getVerticesCount());
            VertexChange[] changes = generateVertexChanges(solution);

            startTime = System.currentTimeMillis();
            cost2Results = runCost2Test(g, solution, changes);
            cost2CurrentTime = System.currentTimeMillis() - startTime;

            startTime = System.currentTimeMillis();
            articleFuncResults = runArticleFuncTest(g, solution, changes);
            articleFuncCurrentTime = System.currentTimeMillis() - startTime;

            if (!areResultsEqual(articleFuncResults, cost2Results)) {
                Utils.log("Results not equal");
            } else {
                articleFuncTotalTime += articleFuncCurrentTime;
                cost2TotalTime += cost2CurrentTime;

                Utils.log("Cost2Time: " + cost2CurrentTime);
                Utils.log("AFuncTime: " + articleFuncCurrentTime);
            }

        }
        Utils.log("------------------------------");
        Utils.log("Total times:");
        Utils.log("Cost2: " + cost2TotalTime);
        Utils.log("AFunc: " + articleFuncTotalTime);
    }

    private int[] runCost2Test(Graph g, int[] startSolution,
            VertexChange[] changes) {
        int[] results = new int[changes.length];

        Cost cost = new CostFromArticle(g, startSolution);

        for (int i = 0; i < changes.length; i++) {
            results[i] = cost.costAfterChange(changes[i]);
        }

        return results;
    }

    private int[] runArticleFuncTest(Graph g, int[] startSolution,
            VertexChange[] changes) {
        int[] results = new int[changes.length];
        int[] localSolution = startSolution.clone();

        for (int i = 0; i < changes.length; i++) {
            localSolution[changes[i].getVertexId()] = changes[i].getNewColor();
            results[i] = Utils.naiveArticleCostFunction(localSolution, g);
        }

        return results;
    }

    private boolean areResultsEqual(int[] r1, int[] r2) {
        if (r1.length != r2.length) return false;

        for (int i = 0; i < r1.length; i++) {
            if (r1[i] != r2[i]) return false;
        }
        return true;
    }

    private VertexChange[] generateVertexChanges(int[] solution) {
        VertexChange[] changes = new VertexChange[noIterationsPerGraph];
        int[] localSolution = solution.clone();

        for (int i = 0; i < noIterationsPerGraph; i++) {
            int noColors = Utils.getColorsMap(localSolution).size();
            int id = random.nextInt(localSolution.length);
            int newColor = random.nextInt(noColors + 1);

            int tries = 0;
            while (newColor == localSolution[id]) {
                newColor = random.nextInt(noColors + 1);
                if (++tries == 10000) {
                    Utils.log("Tries over 10000");
                    return null;
                }
            }
            VertexChange vc = changeVertexColor(localSolution, id, newColor);
            changes[i] = vc;
        }

        return changes;
    }

    private int[] initLegal(int size) {
        int[] solution = new int[size];
        for (int i = 0; i < size; i++) {
            solution[i] = i;
        }
        return solution;
    }

    private VertexChange changeVertexColor(int[] solution, int id, int color) {
        int oldColor = solution[id];
        solution[id] = color;
        return new VertexChange(id, oldColor, color, true);
    }
}
